package made.ga;

import java.util.Random; // for generating random numbers
import java.util.ArrayList;

import made.util.Config;
import made.util.Logger;

public class ChromosomeInt implements Config
{
    private ArrayList<Integer> _genes;
    private int _size;
    private float _fitness;
    private int[][] _limits;

    ChromosomeInt(int size)
    {
        init(size);
    }

    public void init(int size)
    {
        _genes = new ArrayList<Integer>(_size);
        _size = size;
        _fitness = 0.0f;
        _limits = new int[size][2];
        _limits[0][0] = 0;
        _limits[0][1] = 200;
        _limits[1][0] = 0;
        _limits[1][1] = 153;
        _limits[2][0] = 0;
        _limits[2][1] = 153;

        Random initGenerator = new Random();

        for (int i = 0; i < _size; i++)
        {
            int offsetRange = (_limits[i][1] - _limits[i][0]);
            float factor = initGenerator.nextFloat();
            int newValue = Math.round(_limits[i][0] + factor * offsetRange);
            _genes.add(newValue);
        }
    }

    public ChromosomeInt[] reproduce(ChromosomeInt other)
    {
        ChromosomeInt[] result = new ChromosomeInt[2];
        result[0] = new ChromosomeInt(_size);
        result[1] = new ChromosomeInt(_size);

        Random crossoverGenerator = new Random();

        int crossoverPoint = crossoverGenerator.nextInt(_size);

        // int crossoverPoint = _size / 2;

        for (int i = 0; i <= crossoverPoint - 1; i++)
        {
            result[0].setGene(i, other.getGene(i));
            result[1].setGene(i, _genes.get(i));

        }

        for (int i = crossoverPoint; i < _size; i++)
        {
            result[0].setGene(i, _genes.get(i));
            result[1].setGene(i, other.getGene(i));
        }

        return result;
    }

    public void mutate()
    {
        Random indexGenerator = new Random();
        Random mutationOffsetGenerator = new Random();

        int mutationIndex = indexGenerator.nextInt(CHROMOSOME_SIZE);
        double gaussian = mutationOffsetGenerator.nextGaussian();
        double offsetRange = 0.1 * (_limits[mutationIndex][1] - _limits[mutationIndex][0]);
        int mutationOffset = (int) Math.round(gaussian * offsetRange);
        int mutatedValue = _genes.get(mutationIndex) + mutationOffset;

        if (mutatedValue < _limits[mutationIndex][0])
            mutatedValue = _limits[mutationIndex][0];

        if (mutatedValue > _limits[mutationIndex][1])
            mutatedValue = _limits[mutationIndex][1];

        if (MPM_DEBUG_LEVEL == DEBUG_LEVEL.VERBOSE)
            Logger.logLine("Mutated gene " + mutationIndex + " old value was " + _genes.get(mutationIndex) + ", new value is " + mutatedValue);

        _genes.set(mutationIndex, mutatedValue);
    }

    public void setFitness(float value)
    {
        _fitness = value;
        // IO.logLine("Fitness set to: " + _fitness);
    }

    public float getFitness()
    {
        return _fitness;
    }

    public Integer getGene(int index)
    {
        return _genes.get(index);
    }

    public void setGene(int index, Integer value)
    {
        if (_genes.size() > index)
            _genes.set(index, value);
        else
            _genes.add(index, value);
    }

    public ArrayList<Integer> getPhenotype()
    {
        return _genes;
    }

    public String toString()
    {
        String str = "";

        for (int i = 0; i < _size; i++)
        {
            if (i != _size - 1)
                str += _genes.get(i) + " ";
            else
                str += _genes.get(i);
        }

        return str;
    }
};